package producersConsumersTests.WaitingTime;

import java.util.LinkedList;
import java.util.List;

import prod_cons.TaskContainer;
import producersConsumersTests.WaitingTime.TimedTask.WhenToMeasure;
import producersConsumersTests.utils.Test;
import producersConsumersTests.utils.Tester;
import producersConsumersTests.utils.TesterVisitor;

/**
 * Test #4 - waiting time test
 * 
 * This test measures the times between insertion to the container and removal
 * from the container, by saving those within the tasks. The inserters save the
 * insertion time, the removers save the removal time and the task itself.
 * 
 * After the run, we loop over the saved tasks and get the results.
 * 
 * 
 */
public class WaitingTimeTest implements Test {
	private static class WaitingTimeTester extends
			Tester<TimeSettingInserter, TimeSettingRemover> {
		List<TimedTask> tasks = new LinkedList<TimedTask>();
		private double average;

		public WaitingTimeTester(TimeSettingInserter[] inserters,
				TimeSettingRemover[] removers, int timeToRun,
				TaskContainer container) {
			super(inserters, removers, timeToRun, container);
		}

		@Override
		protected void afterRun() {
			for (TimeSettingRemover remover : removers) {
				tasks.addAll(remover.getMeasures());
			}
			System.out.println("Test complete!");
			long sum = 0;
			for (TimedTask t : tasks) {
				sum += (t.getExitTime() - t.getEnterTime());
			}
			average = (double)sum/tasks.size();
		}

		@Override
		protected void beforeRun() {
		}

		public List<TimedTask> getResults() {
			return tasks;
		}

		public double getAverage() {
			return average;
		}
	}

	private final WhenToMeasure insertersWhen, removersWhen;
	private WaitingTimeTester tester;

	public WaitingTimeTest(WhenToMeasure insertersWhen,
			WhenToMeasure removersWhen) {
		this.insertersWhen = insertersWhen;
		this.removersWhen = removersWhen;
	}

	@Override
	public void test(int insertersNum, int removersNum, int peakLength,
			int timeBetweenPeaks, int timeToRun, TaskContainer container,
			int extraInt) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException,
			InterruptedException {
		TimeSettingInserter inserters[] = new TimeSettingInserter[insertersNum];
		for (int i = 0; i < insertersNum; i++)
			inserters[i] = new TimeSettingInserter(container, peakLength,
					timeBetweenPeaks, insertersWhen);
		TimeSettingRemover removers[] = new TimeSettingRemover[removersNum];
		for (int i = 0; i < removersNum; i++)
			removers[i] = new TimeSettingRemover(container, removersWhen);
		tester = new WaitingTimeTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	@Override
	public void test(int insertersNum, int removersNum, double lambda,
			int timeToRun, TaskContainer container, int extraInt)
			throws InterruptedException, InstantiationException,
			IllegalAccessException, ClassNotFoundException {
		TimeSettingInserter inserters[] = new TimeSettingInserter[insertersNum];
		for (int i = 0; i < insertersNum; i++)
			inserters[i] = new TimeSettingInserter(container, lambda,
					insertersWhen);
		TimeSettingRemover removers[] = new TimeSettingRemover[removersNum];
		for (int i = 0; i < removersNum; i++)
			removers[i] = new TimeSettingRemover(container, removersWhen);
		tester = new WaitingTimeTester(inserters, removers, timeToRun,
				container);
		tester.runTest();
	}

	public List<TimedTask> getResults() {
		return tester.getResults();
	}

	public WhenToMeasure getInserterWhen() {
		return this.insertersWhen;
	}
	
	public WhenToMeasure getRemoverWhen() {
		return this.removersWhen;
	}
	
	@Override
	public void accept(TesterVisitor v) {
		v.visit(this);
	}

	public double getAverage() {
		return tester.getAverage();
	}
}
